/**
 * @file    oled_hal.c
 * @author  Azolla (1228449928@qq.com)
 * @brief   OLED[SSD1306] SPI驱动 算法: 位插法(自创)
 * @version v0.1
 * @date    2022-11-15
 * 
 * @copyright Copyright (c) 2022
 * */
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/spi_master.h"
#include "driver/gpio.h"
#include "esp_log.h"

#include "oled_hal.h"

#define TAG  "OLED"
 
#define OLED_HOST    SPI2_HOST
#define PIN_NUM_DC   9
#define PIN_NUM_RST  10
#define PIN_NUM_CLK  12
#define PIN_NUM_MOSI 11
#define PIN_NUM_MISO -1
#define PIN_NUM_CS   -1

//To speed up transfers, every SPI transfer sends a bunch of lines. This define specifies how many. More means more OLED_GRAM use,
//but less overhead for setting up / finishing transfers. Make sure 240 is dividable by this.
#define PARALLEL_LINES  8

static spi_device_handle_t spi;
 
// Workaround: The driver depends on some data in the flash and cannot be placed to DRAM easily for
// now. Using the version in LL instead.
#define gpio_set_level  gpio_set_level_patch
#include "hal/gpio_ll.h"
static inline esp_err_t gpio_set_level_patch(gpio_num_t gpio_num, uint32_t level)
{
    gpio_ll_set_level(&GPIO, gpio_num, level);
    return ESP_OK;
}
 
#define OLED_RES_HIGH()	gpio_set_level(PIN_NUM_RST, 1)
#define OLED_RES_LOW()	gpio_set_level(PIN_NUM_RST, 0)

//This function is called (in irq context!) just before a transmission starts. It will
//set the D/C line to the value indicated in the user field.
void lcd_spi_pre_transfer_callback(spi_transaction_t *t)
{
    int dc = (int)t->user;
    gpio_set_level(PIN_NUM_DC, dc);
}

void oled_spi_init(void)
{
    // ESP_LOGI(TAG, "Initializing bus SPI%d...", OLED_HOST+1);
    spi_bus_config_t buscfg = {
        .miso_io_num = PIN_NUM_MISO,
        .mosi_io_num = PIN_NUM_MOSI,
        .sclk_io_num = PIN_NUM_CLK,
        .quadwp_io_num = -1,
        .quadhd_io_num = -1,
        .max_transfer_sz = 1 * 128
    };
    spi_device_interface_config_t devcfg = {
        .clock_speed_hz = 40 * 1000 * 1000,      // Clock out at 20 MHz
        .mode = 0,                               // SPI mode 0
        .spics_io_num = PIN_NUM_CS,              // CS pin
        .queue_size = 3,                         // We want to be able to queue 3 transactions at a time
        .pre_cb = lcd_spi_pre_transfer_callback, // Specify pre-transfer callback to handle D/C line
    };
    // Initialize the SPI bus
    ESP_ERROR_CHECK( spi_bus_initialize(OLED_HOST, &buscfg, SPI_DMA_CH_AUTO) );
    // Attach the OLCD to the SPI bus
    ESP_ERROR_CHECK( spi_bus_add_device(OLED_HOST, &devcfg, &spi) );

    gpio_config_t io_conf;
    // disable interrupt
    io_conf.intr_type = GPIO_INTR_DISABLE;
    // set as output mode
    io_conf.mode = GPIO_MODE_OUTPUT;
    // bit mask of the pins that you want to set,e.g.
    io_conf.pin_bit_mask = BIT64(PIN_NUM_DC) | BIT64(PIN_NUM_RST);
    // disable pull-down mode
    io_conf.pull_down_en = 0;
    // disable pull-up mode
    io_conf.pull_up_en = 0;
    // configure GPIO with the given settings
    gpio_config(&io_conf);
}

/* Send a command to the LCD. Uses spi_device_polling_transmit, which waits
 * until the transfer is complete.
 *
 * Since command transactions are usually small, they are handled in polling
 * mode for higher speed. The overhead of interrupt transactions is more than
 * just waiting for the transaction to complete.
 */
void oled_write_cmd(spi_device_handle_t spi, const uint8_t cmd)
{
    esp_err_t ret;
    spi_transaction_t t;
    memset(&t, 0, sizeof(t));                   // Zero out the transaction
    t.length = 8;                               // Command is 8 bits
    t.tx_buffer = &cmd;                         // The data is the cmd itself
    t.user = (void *)0;                         // D/C needs to be set to 0
    ret = spi_device_polling_transmit(spi, &t); // Transmit!
    assert(ret == ESP_OK);                      // Should have had no issues.
}

/* Send data to the LCD. Uses spi_device_polling_transmit, which waits until the
 * transfer is complete.
 *
 * Since data transactions are usually small, they are handled in polling
 * mode for higher speed. The overhead of interrupt transactions is more than
 * just waiting for the transaction to complete.
 */
void oled_write_data(spi_device_handle_t spi, const uint8_t *data, int len)
{
    esp_err_t ret;
    spi_transaction_t t;
    if (len == 0)
        return;                                 // no need to send anything
    memset(&t, 0, sizeof(t));                   // Zero out the transaction
    t.length = len * 8;                         // Len is in bytes, transaction length is in bits.
    t.tx_buffer = data;                         // Data
    t.user = (void *)1;                         // D/C needs to be set to 1
    ret = spi_device_polling_transmit(spi, &t); // Transmit!
    assert(ret == ESP_OK);                      // Should have had no issues.
}



//向SSD1106写入一个字节。
//dat:要写入的数据/命令
//cmd:数据/命令标志 0,表示命令;1,表示数据;
void oled_write_byte(uint8_t value, uint8_t cmd)
{				  
	if (cmd == OLED_DATA) {
	  oled_write_data(spi, &value, 1);	//写数据	
	} else { 
	  oled_write_cmd(spi, value);	
	}		
}

void oled_set_pos(uint8_t x, uint8_t y)
{
    oled_write_byte(0xb0 + y, OLED_CMD);
    oled_write_byte(((x & 0xf0) >> 4) | 0x10, OLED_CMD);
    oled_write_byte((x & 0x0f) | 0x01, OLED_CMD);
}

//开启OLED显示
void oled_display_on(void)
{
    oled_write_byte(0X8D, OLED_CMD); // SET DCDC命令
    oled_write_byte(0X14, OLED_CMD); // DCDC ON
    oled_write_byte(0XAF, OLED_CMD); // DISPLAY ON
}
//关闭OLED显示
void oled_display_off(void)
{
    oled_write_byte(0X8D, OLED_CMD); // SET DCDC命令
    oled_write_byte(0X10, OLED_CMD); // DCDC OFF
    oled_write_byte(0XAE, OLED_CMD); // DISPLAY OFF
}

#define OLED_X_POXEL    128     
#define OLED_Y_POXEL    64
typedef union {
    uint8_t   U8 [OLED_X_POXEL * 8];
    uint16_t  U16[OLED_X_POXEL * 4];
    uint32_t  U32[OLED_X_POXEL * 2];
    uint64_t  U64[OLED_X_POXEL * 1];
} oled_gram_t;

static oled_gram_t OLED_GRAM; 
// static uint8_t *OLED_GRAM[(OLED_X_POXEL / 8)];  //

void oled_clear(void)
{ 
    memset(OLED_GRAM.U8, 0x00, sizeof(OLED_GRAM.U8));
}

void oled_refresh_full_screen(void)
{
    // Transaction descriptors. Declared static so they're not allocated on the stack; we need this OLED_GRAM even when this
    // function is finished because the SPI driver needs access to it even while we're already calculating the next line.
    esp_err_t ret;
    static spi_transaction_t trans;
    memset(&trans, 0, sizeof(spi_transaction_t));
    trans.length = 128 * 8; // Len is in bytes, transaction length is in bits.
    trans.user = (void *)1; // D/C needs to be set to 1

    // Queue all transactions.
    for (uint8_t y = 0; y < 8; y++) {
        trans.tx_buffer = OLED_GRAM.U8 + 128 * y;     // Data
        ret = spi_device_queue_trans(spi, &trans, portMAX_DELAY);
        assert(ret == ESP_OK);
    }
}
 
const unsigned char Nixietube_16X32[11][64] = {																				//16X32
{	0x00,0x00,0xE0,0xF0,0xF8,0x38,0x38,0x38,0x38,0x38,0x38,0xF8,0xF0,0xE0,0x00,0x00,
	0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,
	0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,
	0x00,0x00,0x0F,0x1F,0x3F,0x38,0x38,0x38,0x38,0x38,0x38,0x3F,0x1F,0x0F,0x00,0x00,},//"0",0�������
{	0x00,0x00,0x00,0x00,0x38,0x38,0x38,0xF8,0xF0,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x38,0x38,0x38,0x3F,0x3F,0x3F,0x38,0x38,0x00,0x00,0x00,0x00,},//"1",1�������
{	0x00,0x00,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0xF8,0xF0,0xE0,0x00,0x00,
	0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xFF,0xFF,0xFF,0x00,0x00,
	0x00,0x00,0xFE,0xFF,0xFF,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x01,0x00,0x00,0x00,
	0x00,0x00,0x0F,0x1F,0x3F,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x00,0x00,},//"2",2�������
{	0x00,0x00,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0xF8,0xF8,0xE0,0x00,
	0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0xFF,0xFF,0xFF,0x00,
	0x00,0x00,0x00,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0xFF,0xFF,0xFE,0x00,
	0x00,0x00,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x3F,0x3F,0x0F,0x00,},//"3",3�������
{	0x00,0x00,0xFC,0xFC,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0xFC,0xFC,0xFC,0x00,0x00,
	0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,
	0x00,0x00,0x07,0x0F,0x1F,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0xFF,0xFF,0xFF,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x3F,0x3F,0x00,0x00,},//"4",4�������
{	0x00,0x00,0xE0,0xF0,0xF8,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x00,0x00,
	0x00,0x00,0xFF,0xFF,0xFF,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x01,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0xFF,0xFF,0xFE,0x00,0x00,
	0x00,0x00,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x3F,0x1F,0x0F,0x00,0x00,},//"5",5�������
{	0x00,0x00,0xE0,0xF0,0xF8,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x00,
	0x00,0x00,0xFF,0xFF,0xFF,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xE0,0xC0,0x80,0x00,
	0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,
	0x00,0x00,0x0F,0x1F,0x3F,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x3F,0x1F,0x0F,0x00,},//"6",6�������
{	0x00,0x00,0x00,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0xF8,0xF8,0xE0,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x3F,0x3F,0x00,0x00,},//"7",7�������
{	0x00,0x00,0xE0,0xF0,0xF8,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0xF8,0xF0,0xE0,0x00,
	0x00,0x00,0x7F,0xFF,0xFF,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xFF,0xFF,0x7F,0x00,
	0x00,0x00,0xFF,0xFF,0xFF,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0xFF,0xFF,0xFF,0x00,
	0x00,0x00,0x0F,0x1F,0x3F,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x3F,0x1F,0x0F,0x00,},//"8",8�������
{	0x00,0x00,0xE0,0xF0,0xF8,0x38,0x38,0x38,0x38,0x38,0x38,0xF8,0xF8,0xE0,0x00,0x00,
	0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0x00,
	0x00,0x00,0x01,0x03,0x07,0x07,0x07,0x07,0x07,0x07,0x07,0xFF,0xFF,0xFD,0x00,0x00,
	0x00,0x00,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x38,0x3F,0x1F,0x0F,0x00,0x00,},//"9",9�������
{	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
	0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,}	//"  ",10,�հ�
};

// 画点 
void oled_draw_point(uint8_t x, uint8_t y, bool point)
{
    /* OLED位插法 */
    if (x >= OLED_X_POXEL || y >= OLED_Y_POXEL)
        return;
    if (point == true) {
        OLED_GRAM.U64[x] |= (uint64_t)(0x1ULL << y);
    } else {
        OLED_GRAM.U64[x] &= ~(uint64_t)(0x1ULL << y);
    }
}

// 把 列行式  转化为 逐列式
void oled_matrix_convert_16x32(uint8_t *out, const uint8_t *in)
{
    for (uint8_t y = 0; y < 16; y++) {
        for (uint8_t x = 0; x < 4; x++) {
            out[y * 4 + x] = in[x * 16 + y]; 
        }
    }
}

void oled_show_number_16x32(uint8_t x, uint8_t y, uint8_t num)
{
    pixel_type_t pixel;
    uint8_t matrix[64];
    uint8_t number[5];
    number[0] = (num / 100) % 10;
    number[1] = (num / 10) % 10;
    number[2] = (num / 1) % 10;
    for (uint8_t n = 0; n < 3; n++) {
        oled_matrix_convert_16x32(matrix, Nixietube_16X32[number[n]]);
        for (uint8_t i = 0; i < 16; i++) {  /* OLED位插法 */
            pixel.u64 = 0;
            OLED_GRAM.U64[x + i + n * 16] &= ~(uint64_t)(0xFFFFFFFFULL << y);
            memcpy(pixel.u8, matrix + i * 4, 4);
            OLED_GRAM.U64[x + i + n * 16] |= (pixel.u64 << y);
        }
    }
} 

// 动态显示
void oled_dynamic_show_number_16x32(uint8_t x, uint8_t y, uint8_t num)
{
    pixel_type_t pixel;
    uint32_t dynaval = 0;
    uint8_t matrix[64];
    uint8_t number[3];
    number[0] = (num / 100) % 10;
    number[1] = (num / 10) % 10;
    number[2] = (num / 1) % 10;
    // uint8_t numbits = 0;
    // uint8_t temp = num;
    // do {
    //     numbits++;
    //     temp /= 10;
    // } while (temp > 1);  // 计算位数

    for (uint8_t n = 0; n < 3; n++) {
        oled_matrix_convert_16x32(matrix, Nixietube_16X32[number[n]]);
        if (n < 2) {
            for (uint8_t i = 0; i < 16; i++) {  /* OLED位插法 */
                pixel.u64 = 0;
                OLED_GRAM.U64[x + i + n * 16] &= ~(uint64_t)(0xFFFFFFFFULL << y);
                memcpy(pixel.u8, matrix + i * 4, 4);
                OLED_GRAM.U64[x + i + n * 16] |= (pixel.u64 << y);
            }
            continue;
        }
        for (uint8_t b = 32; b > 0; b--) { 
            dynaval |= 0x01;
            for (uint8_t i = 0; i < 16; i++) {  /* OLED位插法 */
                pixel.u64 = OLED_GRAM.U64[x + i + n * 16];
                OLED_GRAM.U64[x + i + n * 16] &= ~(uint64_t)(0xFFFFFFFFULL << y);
                // memcpy(pixel.u8, matrix_old + i * 4, 4);
                pixel.u64 >>= (33 - b);
                OLED_GRAM.U64[x + i + n * 16] |= (pixel.u64 << y);
                 
                pixel.u64 = 0;
                memcpy(pixel.u8, matrix + i * 4, 4);
                pixel.u64 &= dynaval;
                OLED_GRAM.U64[x + i + n * 16] |= (pixel.u64 << (y + b - 1));
            }
            dynaval <<= 1;
            oled_refresh_full_screen();  // 刷新全屏
            vTaskDelay(50);
        }
    }
} 

void drawcircle(int x0, int y0, int radius)
{
    int x = radius;
    int y = 0;
    int err = 0;

    while (x >= y)
    {
        oled_draw_point(x0 + x, y0 + y, 1);
        oled_draw_point(x0 + y, y0 + x, 1);
        oled_draw_point(x0 - y, y0 + x, 1);
        oled_draw_point(x0 - x, y0 + y, 1);
        oled_draw_point(x0 - x, y0 - y, 1);
        oled_draw_point(x0 - y, y0 - x, 1);
        oled_draw_point(x0 + y, y0 - x, 1);
        oled_draw_point(x0 + x, y0 - y, 1);

        if (err <= 0) {
            y += 1;
            err += 2 * y + 1;
        }

        if (err > 0) {
            x -= 1;
            err -= 2 * x + 1;
        }
    }
}



/**
 * @brief	oled init config
 *
 * @param   void
 *
 * @return  void
 */
void oled_init_config(void)
{
	oled_spi_init();
	OLED_RES_HIGH();
	vTaskDelay(10);
	OLED_RES_LOW();
	vTaskDelay(20);
	OLED_RES_HIGH();
    // OLED资料: https://blog.csdn.net/gengyuchao/article/details/86608037
    oled_write_byte(0xAE, OLED_CMD); //--turn off oled panel
    oled_write_byte(0x02, OLED_CMD); //---set low column address
    oled_write_byte(0x10, OLED_CMD); //---set high column address
    oled_write_byte(0x40, OLED_CMD); //--set start line address  Set Mapping RAM Display Start Line (0x00~0x3F)
    oled_write_byte(0x81, OLED_CMD); //--set contrast control register
    oled_write_byte(0xCF, OLED_CMD); // Set SEG Output Current Brightness
    oled_write_byte(0xA1, OLED_CMD); //--Set SEG/Column Mapping      0xa0左右反置 0xa1正常
    oled_write_byte(0xC8, OLED_CMD); // Set COM/Row Scan Direction   0xc0上下反置 0xc8正常
    oled_write_byte(0xA6, OLED_CMD); //--set normal display
    oled_write_byte(0xA8, OLED_CMD); //--set multiplex ratio(1 to 64)
    oled_write_byte(0x3f, OLED_CMD); //--1/64 duty
    oled_write_byte(0x81, OLED_CMD); //对比度设置
    oled_write_byte(0x5F, OLED_CMD); // 1~255;默认0X7F (亮度设置,越大越亮)
    oled_write_byte(0xD3, OLED_CMD); //-set display offset	Shift Mapping RAM Counter (0x00~0x3F)
    oled_write_byte(0x00, OLED_CMD); //-not offset
    oled_write_byte(0xd5, OLED_CMD); //--set display clock divide ratio/oscillator frequency
    oled_write_byte(0x80, OLED_CMD); //--set divide ratio, Set Clock as 100 Frames/Sec
    oled_write_byte(0xD9, OLED_CMD); //--set pre-charge period
    oled_write_byte(0xF1, OLED_CMD); // Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
    oled_write_byte(0xDA, OLED_CMD); //--set com pins hardware configuration
    oled_write_byte(0x12, OLED_CMD); 
    oled_write_byte(0xDB, OLED_CMD); //--set vcomh
    oled_write_byte(0x40, OLED_CMD); // Set VCOM Deselect Level
    oled_write_byte(0x20, OLED_CMD); //-Set Page Addressing Mode (0x00/0x01/0x02) 0x20
    oled_write_byte(0x01, OLED_CMD); // Set 页地址模式(A[1:0]=10b) 水平地址模式(A[1:0]=00b) 垂直地址模式(A[1:0]=01b)
    oled_write_byte(0x8D, OLED_CMD); //--set Charge Pump enable/disable
    oled_write_byte(0x14, OLED_CMD); //--set(0x10) disable
    oled_write_byte(0xA4, OLED_CMD); // Disable Entire Display On (0xa4/0xa5)
    oled_write_byte(0xA6, OLED_CMD); // Disable Inverse Display On (0xa6/a7)
    oled_write_byte(0xAF, OLED_CMD); //--turn on oled panel

    oled_clear();

#if 1  // test...
    ESP_LOGI(TAG, "OLED Start Test..."); 
    while (1) {

        int64_t start_time = esp_timer_get_time();
        // OLED_GRAM.U64[0] = 0XFFFFFFFFFFFFFFFFULL;
        // OLED_GRAM.U64[1] = OLED_GRAM.U64[0];
        oled_refresh_full_screen();
        ESP_LOGI(TAG, "spi_device_queue_trans = %lldus", esp_timer_get_time() - start_time);
        ESP_LOGI(TAG, "Free heap, current: %d, minimum: %d", esp_get_free_heap_size(), esp_get_minimum_free_heap_size());  // 打印内存
        vTaskDelay(90);
        
        // oled_show_number_16x32(0, 0, 200);
        // oled_refresh_full_screen();
        // vTaskDelay(1500);

        static uint8_t number = 100;
        oled_dynamic_show_number_16x32(0, 0, number++);
        static bool dir = 0;
        dir = !dir;
        for (uint8_t i = 0; i < 32; i++) {
            oled_draw_point(60 + i, 40, dir);
            oled_draw_point(60 + i, 41, dir);
            oled_draw_point(60 + i, 42, dir);
        }

        drawcircle(80, 40, 20);
        drawcircle(80, 40, 21);
        drawcircle(80, 40, 22);
        drawcircle(80, 40, 23);
        vTaskDelay(500);
        
        #if 0
        static uint8_t x = 10 , y = 10;
   
        
        oled_show_number_16x32(x, y, number++);
 
        x++; y++;
        if (y > 64) {
            y = 0;
            x = 0;
            oled_clear();
        }

        #endif

 
    }
#endif    
}



